package au.edu.unsw.editingtool.web;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.regex.*;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.xpath.XPathExpressionException;

import org.xml.sax.SAXException;

import au.edu.unsw.editingtool.db.DBClaim;
import au.edu.unsw.editingtool.db.DBEditingpool;
import au.edu.unsw.editingtool.db.DBPublication;
import au.edu.unsw.editingtool.fedora.FedoraFactory;
import au.edu.unsw.editingtool.global.EditingToolException;
import au.edu.unsw.editingtool.global.GlobalConstant;
import au.edu.unsw.editingtool.global.PropertyReader;
import au.edu.unsw.editingtool.global.RedirectPageException;
import au.edu.unsw.editingtool.global.Util;
import au.edu.unsw.editingtool.global.Metadata;
import au.edu.unsw.editingtool.transformxml.XMLProcessor;

/**
 * Servlet implementation class Editing
 */
public class Edit extends HttpServlet {
	private static final long serialVersionUID = 1L;	
	private String ServletContext = null;
	private String ApplicationContext = null;
	private String ApplicationPath = null;
	
    /**
     * @see HttpServlet#HttpServlet()
     */
    public Edit() {
        super();
        // TODO Auto-generated constructor stub        
    }

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		this.doService(request, response);
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		this.doService(request, response);
	}
	
	protected void doService(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		ServletContext = getServletContext().getRealPath("") + GlobalConstant.fileSeparator;
		ApplicationContext = request.getLocalName()+":"+request.getServerPort()+"/"+
							GlobalConstant.APPLICATIONNAME+"/";
		ApplicationPath = request.getContextPath();
		System.out.println("==== [EDIT Servlet] ====");
		
		// Get the configuration.
		PropertyReader configReader = null;
		try {			
			configReader = new PropertyReader();
			configReader.setFile(ServletContext + GlobalConstant.configFilePath);			
			configReader.buildConfiguration();			
		}
		catch (Exception ex)
		{
			// TODO better Auto-generated catch block
			ex.printStackTrace();
		} 
		
		String valetXML = ""; 
		HttpSession session = request.getSession(false);	
		String sessionid = (String) session.getId();
		
		// Check ctrl and pid for task and publication id from session and querystring.
		// Ignore other var from quesrystring or session because this is edit servlet.
		// Prioritise from quesrystring.
				
		String lastcommand = null;		
		Boolean redirect = false;
		Boolean reedit = false;
		String redirectUrl = null;
		
		String querystring = request.getQueryString();
		String username = null;
		String group = null;
				
		username = (String) session.getAttribute(GlobalConstant.username);
		group = (String) session.getAttribute(GlobalConstant.userGroup);
		
		// Obtain pid parameter
		String pid = null;
		pid = (String) request.getAttribute(GlobalConstant.pid);
		if (pid ==null)
			pid = request.getParameter(GlobalConstant.pid).toString();		
				
		// Obtain Control Command from attribute or from parameter/query string
		String controlcommand = null;
		controlcommand=(String) request.getAttribute(GlobalConstant.requestCommand);
		if(controlcommand==null)
			controlcommand=request.getParameter(GlobalConstant.requestCommand);		

		lastcommand = (String) session.getAttribute(GlobalConstant.requestLastCommand);
		System.out.println("[EDIT] requestLastCommand : "+lastcommand);
		
		// Check controlcommand		
		if(controlcommand == null || controlcommand == "")
		{									
			redirect = true;
			//System.out.println("CTRL PROBLEM1 : "+controlcommand);
		}
		else
		{
			if(controlcommand.compareTo(GlobalConstant.cmdEdit)!=0)
			{
				redirect = true;
			}
		}
				
		//Check pid
		if(pid==null || pid=="")
		{	
			redirect = true;
		}
		
		// Check if Command is Edit or we need to redirect
		if (redirect)
		{	
			session.setAttribute(GlobalConstant.responseType,GlobalConstant.typeError);
			session.setAttribute(GlobalConstant.responseControl,GlobalConstant.cmdEdit);		
			session.setAttribute(GlobalConstant.requestLastCommand, GlobalConstant.cmdEdit);
			session.setAttribute(GlobalConstant.requestLastCommand,GlobalConstant.cmdEdit);
			redirectUrl=GlobalConstant.JSPMain+"?"+querystring;
			
			System.out.println("[EDIT] Forward to "+GlobalConstant.JSPMain);
			RequestDispatcher rd = request.getRequestDispatcher(redirectUrl);
			rd.forward(request, response);
			return;
		}
		
		//System.out.println("Redirect URL: "+redirectUrl);
		// reset redirect and  redirectUrl for other redirect later.
		redirect = false;		
		redirectUrl = null;									
				
		// Need to make sure that FormDataFS and FormData in Session is not empty 
		// to know if it is actually a reedit step		
		if(lastcommand!=null)
		{		
			if(lastcommand.compareTo(GlobalConstant.cmdPreview)==0)
			{						
				Hashtable<String,String> tb = (Hashtable<String,String>) session.getAttribute(GlobalConstant.requestFormData);
				String js = Metadata.transformHashToJavaScriptAssociativeArray(tb);
				request.setAttribute(GlobalConstant.requestFormDataJS, js);				
				session.setAttribute(GlobalConstant.requestFormData,tb);							
				reedit = true;				
			}			
		}
		if(!reedit)
		//New edit page.
		//Get ValetXML
		{							
			try {				
				// 1. Check Editing pool table if user can edit
				boolean isEditRecordExist = this.isEditExist(pid,sessionid);				
								
				if(isEditRecordExist)
				{									
					if(!this.canEdit(pid, username))
					{
						System.out.println("[EDIT] "+username+" can not edit resource "+pid);
						System.out.println("[EDIT] Forward to "+GlobalConstant.JSPError);					
						request.setAttribute(GlobalConstant.responseType,GlobalConstant.typeError);
						request.setAttribute(GlobalConstant.responseControl, GlobalConstant.cmdEdit);
						request.setAttribute(GlobalConstant.responseMsg, "Currently this resource "+pid+" is being edited by other user!");
						session.setAttribute(GlobalConstant.requestLastCommand,GlobalConstant.cmdEdit);
						RequestDispatcher rd = request.getRequestDispatcher(GlobalConstant.JSPError);
						rd.forward(request, response);
						return;
					}					
				}	
				
				// 2. Check the publication record
				DBPublication dbpublication = new DBPublication();
				dbpublication.setConfFile(ServletContext + GlobalConstant.configFilePath);
				boolean isPublicationExist = dbpublication.getData(pid);
				boolean canEdit = false;
				if(!isPublicationExist)
				{
					canEdit = true;
				}
				else
				{
					String publicationEditor = null;
					publicationEditor = dbpublication.getEditor();
					if(publicationEditor!=null || publicationEditor.length()!=0)
					{
						if(group.compareTo(GlobalConstant.groupUser)==0 && username.compareTo(publicationEditor)==0)
							canEdit=true;
						else
							canEdit=false;
					}	
						
					String publicationStatus = null;
					publicationStatus = dbpublication.getObjectStatus();
					if(publicationStatus!=null || publicationStatus.length()!=0)
					{
						if(group.compareTo(GlobalConstant.groupUser)==0 && 
								(publicationStatus.compareTo(GlobalConstant.objectNew)==0 || publicationStatus.compareTo(GlobalConstant.objectApproved)==0 ))
							canEdit=true;
						else if (group.compareTo(GlobalConstant.groupReview)==0)
							canEdit=true;
						else
							canEdit=false;							
					}
					
					if (group.compareTo(GlobalConstant.groupAdmin)==0 || group.compareTo(GlobalConstant.groupSysAdmin)==0)
						canEdit=true;
				}
				
				if(!canEdit)
				{
					System.out.println("[EDIT] "+username+" can not edit resource "+pid);
					System.out.println("[EDIT] Forward to "+GlobalConstant.JSPError);					
					request.setAttribute(GlobalConstant.responseType,GlobalConstant.typeError);
					request.setAttribute(GlobalConstant.responseControl, GlobalConstant.cmdEdit);
					request.setAttribute(GlobalConstant.responseMsg, "Currently this resource "+pid+" is under review");
					session.setAttribute(GlobalConstant.requestLastCommand,GlobalConstant.cmdEdit);
					RequestDispatcher rd = request.getRequestDispatcher(GlobalConstant.JSPError);
					rd.forward(request, response);
					return;
				}
							
				// Instantiate Metadata object
				// 3. Set output file
				// 	  set xml file to be written for getmetadata function
				//    set xsl file for transform function

				Metadata metadata = new Metadata(); 
				metadata.setOuputFile(username+"_"+pid+"_VALETXML", "xml");			
				metadata.setXMLFile(username+"_"+pid,"xml");
				metadata.setXSLFile(ServletContext + 
					configReader.getConfigValue(new String[]{GlobalConstant.TRANSFORM,"root"})+
					GlobalConstant.fileSeparator +
					configReader.getConfigValue(new String[]{GlobalConstant.TRANSFORM,"template"})+
					GlobalConstant.fileSeparator +
					configReader.getConfigValue(new String[]{GlobalConstant.TRANSFORM,"tovalet"}));
			
				//System.out.println("OUTPUTFILE: "+metadata.getOuputFile());
				//System.out.println("XMLFILE: "+metadata.getXMLFile());
				//System.out.println("XSLFILE: "+metadata.getXSLFile());
			
				// 4. Construct fedora url from config
				String fedoraurl = configReader.getConfigValue(new String[]{GlobalConstant.FEDORA,"host"})+ 
						":" + 
						configReader.getConfigValue(new String[]{GlobalConstant.FEDORA,"port"}) +						
						configReader.getConfigValue(new String[]{GlobalConstant.FEDORA,"path"});
			
				//System.out.println("FEDORAURL: "+fedoraurl);
			
				// 5. Get the metadata. The result is stored in xmlfile var in metadata object
				boolean isPidExist = metadata.getMetadata( fedoraurl, 
					configReader.getConfigValue(new String[]{GlobalConstant.FEDORA,"username"}), 
					configReader.getConfigValue(new String[]{GlobalConstant.FEDORA,"password"}), 
					pid, 
					configReader.getConfigValue(new String[]{GlobalConstant.FEDORA,"metadata"}));
			
				if(!isPidExist)
				{
					System.out.println("[EDIT] Metadata "+pid+" is not exists!");
					System.out.println("[EDIT] Forward to "+GlobalConstant.JSPError);					
					request.setAttribute(GlobalConstant.responseType,GlobalConstant.typeError);
					request.setAttribute(GlobalConstant.responseControl, GlobalConstant.cmdEdit);
					request.setAttribute(GlobalConstant.responseMsg, "Resource "+pid+" is not exists!");
					session.setAttribute(GlobalConstant.requestLastCommand,GlobalConstant.cmdEdit);
					RequestDispatcher rd = request.getRequestDispatcher(GlobalConstant.JSPError);
					rd.forward(request, response);
					return;
				}
				

				// 6. Transform the metadata using xslfile in metadata object and
				//	  the result file location is stored in output var in metadata object
				metadata.transform();
				
				// 7. Store the js associative array and hashtable of the result from XML
				String js = metadata.transformHashToJavaScriptAssociativeArray();
				session.setAttribute(GlobalConstant.requestFormData,metadata.transformOuputFileToHash());															

				// 8. Check if the user have claimed the resource.
				String claimStatus  = "";
				
				claimStatus = this.getClaimStatus(username,group,pid);
				System.out.println("[EDIT] claimStatus : "+claimStatus );
				if(group == null)
					group = "";				
				if (group.compareTo(GlobalConstant.groupUser)==0 || group.length()==0)
				{					
					if(claimStatus==null)					
					{	
						System.out.println("[EDIT] "+username+" has not claimed the resource "+pid);
						System.out.println("[EDIT] Forward to CLAIM Servlet");					
						request.setAttribute(GlobalConstant.requestCommand,GlobalConstant.cmdClaimPreview);
						session.setAttribute(GlobalConstant.requestLastCommand,GlobalConstant.cmdEdit);
						RequestDispatcher rd = request.getRequestDispatcher(GlobalConstant.ServletClaim);
						rd.forward(request, response);
						return;
					}
					else if(claimStatus.compareTo(GlobalConstant.claimClaimed)!=0)
					{
						System.out.println("[EDIT] "+username+" claim request for resource "+pid+" has not been approved yet");
						System.out.println("[EDIT] Forward to "+GlobalConstant.JSPError);					
						request.setAttribute(GlobalConstant.responseType,GlobalConstant.typeError);
						request.setAttribute(GlobalConstant.responseControl, GlobalConstant.cmdEdit);
						request.setAttribute(GlobalConstant.responseMsg, "The claim request is under review");
						session.setAttribute(GlobalConstant.requestLastCommand,GlobalConstant.cmdEdit);
						RequestDispatcher rd = request.getRequestDispatcher(GlobalConstant.JSPError);
						rd.forward(request, response);
						return;
					}
				}
				
				// 9. Store Java script AssociativeArray on session
				request.setAttribute(GlobalConstant.requestFormDataJS, js);
				
				// 10. Add new record on editing pool table if there is no record				
				if(!isEditRecordExist)
				{
					System.out.println("[EDIT] ADD record in editingpool table in database");
					this.addEditRecord(pid,username,sessionid);
				}
			}
			catch (Exception ex) {
				//TODO
				// If there is a crash make sure that the editing pool is deleted.
				ex.printStackTrace();
			}
		}		
		
		System.out.println("[EDIT] Forward to "+GlobalConstant.JSPFormdata);
		System.out.println("[EDIT] PID = "+pid);
		session.setAttribute(GlobalConstant.requestLastCommand,GlobalConstant.cmdEdit);
		request.setAttribute(GlobalConstant.pid,pid);
		RequestDispatcher rd = request.getRequestDispatcher(GlobalConstant.JSPFormdata);
		rd.forward(request, response);			
	}
	
	private String getClaimStatus (String username, String group, String pid)
	{
		String result = null;
		if(username==null || username.length()==0 || group==null || group.length()==0 || pid==null || pid.length()==0)
			return result;
		
		if(group.compareTo(GlobalConstant.groupSysAdmin)==0 || group.compareTo(GlobalConstant.groupAdmin)==0)
		{
			System.out.println("[EDIT] getClaimStatus : group is "+GlobalConstant.groupSysAdmin+" or "+GlobalConstant.groupAdmin);
			return result;
		}
		
		DBClaim dbclaim = new DBClaim();
		dbclaim.setConfFile(ServletContext + GlobalConstant.configFilePath);
		
		try {
			result = dbclaim.getStatus(username,pid);
		} 
		catch (Exception e) {			
			e.printStackTrace();
		}				
		return result;
	}
	
	private boolean isEditExist(String pid, String sessionid)
	{
		boolean result = false;
		DBEditingpool dbeditingpool = new DBEditingpool();
		dbeditingpool.setConfFile(ServletContext + GlobalConstant.configFilePath);
		try {
			result = dbeditingpool.isExist(pid,sessionid);
		} 
		catch (Exception e) {			
			e.printStackTrace();
		}
		return result;	
	}
	
	private boolean canEdit(String pid, String username)
	{
		boolean result = false;		
		String dbusername = null;
		DBEditingpool dbeditingpool = new DBEditingpool();
		dbeditingpool.setConfFile(ServletContext + GlobalConstant.configFilePath);
		try 
		{
			dbusername = dbeditingpool.getUsername(pid);
			if(dbusername!=null && username.compareTo(dbusername)==0)
			{
				result=true;
			} 			
		}		
		catch (Exception e) {			
			e.printStackTrace();
		}
		return result;
	}
	
	private void addEditRecord(String pid, String username, String sessionid)
	{
		DBEditingpool dbeditingpool = new DBEditingpool();
		dbeditingpool.setConfFile(ServletContext + GlobalConstant.configFilePath);
		try {
			dbeditingpool.addRecord(pid,username,sessionid);
		} 
		catch (Exception e) {			
			e.printStackTrace();
		}
	}
		
}
